package in.rinisoft.tools.sonar.maven.plugin;

import com.google.common.collect.Maps;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.project.MavenProject;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.utils.SonarException;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * Code has been taken from Sonar maven plugin and modified to suit the need
 * where ever needed
 * 
 * @author Neeraj Mittal 
 */
public class MavenProjectConverter {

	private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "";

	public static ProjectDefinition convert(List<MavenProject> poms,
			MavenProject root) {
		Map<String, MavenProject> paths = Maps.newHashMap(); // projects by
																// canonical
																// path to
																// pom.xml
		Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();

		try {
			for (MavenProject pom : poms) {
				paths.put(pom.getFile().getCanonicalPath(), pom);
				defs.put(pom, convert(pom));
			}

			for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
				MavenProject pom = entry.getValue();
				for (Object m : pom.getModules()) {
					String moduleId = (String) m;
					File modulePath = new File(pom.getBasedir(), moduleId);
					if (modulePath.exists() && modulePath.isDirectory()) {
						modulePath = new File(modulePath, "pom.xml");
					}
					MavenProject module = paths.get(modulePath
							.getCanonicalPath());

					ProjectDefinition parentProject = defs.get(pom);
					ProjectDefinition subProject = defs.get(module);
					if (parentProject == null) {
						throw new IllegalStateException(
								UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
					}
					if (subProject == null) {
						throw new IllegalStateException(
								UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
					}
					parentProject.addSubProject(subProject);
				}
			}
		} catch (IOException e) {
			throw new SonarException(e);
		}

		ProjectDefinition rootProject = defs.get(root);
		if (rootProject == null) {
			throw new IllegalStateException(
					UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
		}

		return rootProject;
	}

	/**
	 * Visibility has been relaxed for tests.
	 */
	static ProjectDefinition convert(MavenProject pom) {
		String key = new StringBuilder().append(pom.getGroupId()).append(":")
				.append(pom.getArtifactId()).toString();
		ProjectDefinition definition = ProjectDefinition.create();
		// IMPORTANT NOTE : reference on properties from POM model must not be
		// saved, instead they should be copied explicitly - see SONAR-2896
		definition.setProperties(pom.getModel().getProperties()).setKey(key)
				.setVersion(pom.getVersion()).setName(pom.getName())
				.setDescription(pom.getDescription())
				.addContainerExtension(pom);
		synchronizeFileSystem(pom, definition);

		String languages = pom.getProperties().getProperty(
				"sonar.aditional.languages");

		if (StringUtils.isNotBlank(languages)) {

			for (String lang : languages.split(",")) {

				definition.addSubProject(convertForLanguage(pom, lang));
			}
		}
		return definition;
	}

	static ProjectDefinition convertForLanguage(MavenProject pom,
			String language) {
		String key = new StringBuilder().append(pom.getGroupId()).append(":")
				.append(pom.getArtifactId()).append("-").append(language)
				.toString();
		ProjectDefinition definition = ProjectDefinition.create();
		// IMPORTANT NOTE : reference on properties from POM model must not be
		// saved, instead they should be copied explicitly - see SONAR-2896
		definition.setProperties(pom.getModel().getProperties()).setKey(key)
				.setVersion(pom.getVersion()).setName(pom.getName() + "-" + language)
				.setDescription(pom.getDescription())
				.addContainerExtension(pom)
				.setProperty("sonar.language", language);
		synchronizeFileSystemLanguage(pom, definition, language);
		return definition;
	}

	@SuppressWarnings("unchecked")
	public static void synchronizeFileSystem(MavenProject pom,
			ProjectDefinition into) {
		into.setBaseDir(pom.getBasedir());
		into.setWorkDir(new File(resolvePath(pom.getBuild().getDirectory(),
				pom.getBasedir()), "sonar"));
		into.setSourceDirs((String[]) pom.getCompileSourceRoots().toArray(
				new String[pom.getCompileSourceRoots().size()]));
		into.setTestDirs((String[]) pom.getTestCompileSourceRoots().toArray(
				new String[pom.getTestCompileSourceRoots().size()]));
	}

	public static void synchronizeFileSystemLanguage(MavenProject pom,
			ProjectDefinition into, String language) {
		into.setBaseDir(pom.getBasedir());
		into.setWorkDir(new File(resolvePath(pom.getBuild().getDirectory(),
				pom.getBasedir()), "sonar"));
		String sourceDir = pom.getProperties().getProperty(
				"sonar." + language + ".sourceDirectory");
		if (StringUtils.isNotBlank(sourceDir)) {
			into.setSourceDirs(new File(pom.getBasedir(), sourceDir));
		}

		String testDir = pom.getProperties().getProperty(
				"sonar." + language + ".testDirectory");

		if (StringUtils.isNotBlank(testDir)) {
			into.setTestDirs(new File(pom.getBasedir(), testDir));
		}

	}

	static File resolvePath(String path, File basedir) {
		File file = new File(path);
		if (!file.isAbsolute()) {
			try {
				file = new File(basedir, path).getCanonicalFile();
			} catch (IOException e) {
				throw new SonarException("Unable to resolve path '" + path
						+ "'", e);
			}
		}
		return file;
	}

	private MavenProjectConverter() {
		// only static methods
	}
}
